Редко возникает необходимость в явном вызове деструктора. Однако может быть полезно выполнить удаление объектов, размещенных по абсолютным адресам. Эти объекты обычно выделяются с помощью определяемого new пользователем оператора, который принимает аргумент размещения. Оператор delete не может освободить эту память, так как он не выделяется из свободного хранилища (дополнительные сведения см. в разделе "Новые и удаленные операторы"). Вызов деструктора, однако, может выполнить соответствующую очистку. Для явного вызова деструктора для объекта (s) класса String воспользуйтесь одним из следующих операторов.
s.String::~String(); // non-virtual call ps->String::~String(); // non-virtual call s.~String(); // Virtual call ps->~String(); // Virtual call
Нотация для явных вызовов деструкторов, показанная в предыдущем примере, может использоваться независимо от того, определяет ли тип деструктор. Это позволяет выполнять такие явные вызовы, не зная, определен ли деструктор для типа. Явный вызов деструктора, если ни один из них не определен, не имеет никакого эффекта.
s.String::~String(); // non-virtual call ps->String::~String(); // non-virtual call s.~String(); // Virtual call ps->~String(); // Virtual call
class Integer
{
private:
int value;
public:
Integer(int i): value(i)
{}
const Integer operator+(const Integer& rv) const {
return (value + rv.value);
}
};
В данном случае, оператор оформлен как член класса, аргумент определяет значение, находящееся в правой части оператора. Вообще, существует два основных способа перегрузки операторов: глобальные функции, дружественные для класса, или подставляемые функции самого класса. Какой способ, для какого оператора лучше, рассмотрим в конце топика.class Integer
{
private:
int value;
public:
Integer(int i): value(i)
{}
//унарный +
friend const Integer& operator+(const Integer& i);
//унарный -
friend const Integer operator-(const Integer& i);
//префиксный инкремент
friend const Integer& operator++(Integer& i);
//постфиксный инкремент
friend const Integer operator++(Integer& i, int);
//префиксный декремент
friend const Integer& operator--(Integer& i);
//постфиксный декремент
friend const Integer operator--(Integer& i, int);
};
//унарный плюс ничего не делает.
const Integer& operator+(const Integer& i) {
return i.value;
}
const Integer operator-(const Integer& i) {
return Integer(-i.value);
}
//префиксная версия возвращает значение после инкремента
const Integer& operator++(Integer& i) {
i.value++;
return i;
}
//постфиксная версия возвращает значение до инкремента
const Integer operator++(Integer& i, int) {
Integer oldValue(i.value);
i.value++;
return oldValue;
}
//префиксная версия возвращает значение после декремента
const Integer& operator--(Integer& i) {
i.value--;
return i;
}
//постфиксная версия возвращает значение до декремента
const Integer operator--(Integer& i, int) {
Integer oldValue(i.value);
i.value--;
return oldValue;
}
Теперь вы знаете, как компилятор различает префиксные и постфиксные версии декремента и инкремента. В случае, когда он видит выражение ++i, то вызывается функция operator++(a). Если же он видит i++, то вызывается operator++(a, int). То есть вызывается перегруженная функция operator++, и именно для этого используется фиктивный параметр int в постфиксной версии.class Integer
{
private:
int value;
public:
Integer(int i): value(i)
{}
friend const Integer operator+(const Integer& left, const Integer& right);
friend Integer& operator+=(Integer& left, const Integer& right);
friend bool operator==(const Integer& left, const Integer& right);
};
const Integer operator+(const Integer& left, const Integer& right) {
return Integer(left.value + right.value);
}
Integer& operator+=(Integer& left, const Integer& right) {
left.value += right.value;
return left;
}
bool operator==(const Integer& left, const Integer& right) {
return left.value == right.value;
}
Во всех этих примерах операторы перегружаются для одного типа, однако, это необязательно. Можно, к примеру, перегрузить сложение нашего типа Integer и определенного по его подобию Float.return Integer(left.value + right.value);
Честно говоря, не знаю, какая ситуация актуальна для C++11, все рассуждения далее справедливы для C++98.Integer temp(left.value + right.value);
return temp;
Но на самом деле, в этом случае произойдет вызов конструктора в первой строке, далее вызов конструктора копирования, который скопирует объект, а далее, при раскрутке стека вызовется деструктор. При использовании первой записи компилятор изначально создаёт объект в памяти, в которую нужно его скопировать, таким образом экономится вызов конструктора копирования и деструктора.class Integer
{
private:
int value;
public:
Integer(int i): value(i) {}
Integer& operator=(const Integer& right) {
//проверка на самоприсваивание
if (this == &right) { return *this; }
value = right.value;
return *this;
}
};
Как можно заметить, в начале функции производится проверка на самоприсваивание. Вообще, в данном случае самоприсваивание безвредно, но ситуация не всегда такая простая. Например, если объект большой, можно потратить много времени на ненужное копирование, или при работе с указателями.
| Оператор | Рекомендуемая форма |
| Все унарные операторы | Член класса |
| = () [] -> ->* | Обязательно член класса |
| += -= /= *= ^= &= |= %= >>= <<= | Член класса |
| Остальные бинарные операторы | Не член класса |
+++ Пример из MSDN:
class Int { public: Int &operator++( int n ); private: int _i; }; Int& Int::operator++( int n ) { if( n != 0 )_i+=n;// Handle case where an argument is passed. else _i++; // Handle case where no argument is passed. return *this; } int main() { Int i; i.operator++( 25 ); // Increment by 25. }
+++ операторы преобразования типов:
class A
{
int x;
public:
A(int _x): x(_x) {}
operator int() const {
return x;
}
};
void foo() {
A a;
int b;
b = a;
}